#include "implot_my_linesplot.h"

#include "implot.h"
#include <cmath>
#include <iostream>

using namespace std;

MyLinesPlot::MyLinesPlot(std::string _name, size_t max_visible_points_)
    : Control(_name), updateData(nullptr), max_visible_points(max_visible_points_)
{
}

MyLinesPlot::~MyLinesPlot() {}

void MyLinesPlot::setUpdateDataFunc(std::function<void()> _func)
{
    this->updateData = _func;
}

void MyLinesPlot::addOrUpdateSeries(const std::string &name, const ImVec2 &point)
{
    auto &points = series_data[name];
    points.push_back(point);
    if (points.size() > max_visible_points)
    {
        points.pop_front();
    }
}

void MyLinesPlot::addOrUpdateSeries(const std::string &name, float y)
{
    auto &points = series_data[name];
    if (points.empty())
    {
        points.resize(max_visible_points);
        for (size_t i = 0; i < max_visible_points; ++i)
        {
            points[i].x = static_cast<float>(i) / (max_visible_points - 1);
            points[i].y = 0.0f;
        }
    }

    // Shift all previous y values to the left
    for (size_t i = 0; i < points.size() - 1; ++i)
    {
        points[i].y = points[i + 1].y;
    }
    // Insert new y at the end
    points.back().y = y;
}

void MyLinesPlot::Show()
{
    if (this->updateData)
    {
        updateData();
    }

    if (series_data.empty())
        return;

    ImGui::Begin(this->name.c_str());
    if (ImPlot::BeginPlot(this->name.c_str()))
    {
        ImPlot::SetupAxes("Time", "KF Gain", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);

        for (auto &[label, points] : series_data)
        {
            if (points.empty())
                continue;

            std::vector<float> xs, ys;
            xs.reserve(points.size());
            ys.reserve(points.size());
            int start = static_cast<int>(points.size()) - static_cast<int>(max_visible_points);
            if (start < 0)
                start = 0;
            for (size_t i = start; i < points.size(); ++i)
            {
                xs.push_back(points[i].x);
                ys.push_back(points[i].y);
            }

            ImPlot::PlotLine(label.c_str(), xs.data(), ys.data(), static_cast<int>(xs.size()));
        }

        ImPlot::EndPlot();
    }
    ImGui::End();
}

void MyLinesPlot::Clear()
{
    this->series_data.clear();
}